import inherits from 'inherits'
import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer'
import { append as svgAppend, create as svgCreate } from 'tiny-svg'
import { componentsToPath } from 'diagram-js/lib/util/RenderUtil'
import { isNumber } from 'min-dash'
import Constant from '../../Constant'
import Common from '../../Common'
import Utils from '../../Utils'

/**
 * 在默认的bpmnjs渲染类基础上的扩展类
 *
 * @param {*} eventBus
 */
export default function PhuketCustomRenderer(eventBus, modeling) {
  // 添加创建shape结束时触发的监听事件
  eventBus.on('create.end', function(event) {
    const shape = event.shape
    const businessObject = shape.businessObject
    const stage = businessObject.$attrs.stage
    const type = businessObject.$attrs.type
    const group = businessObject.$attrs.group
    const title = businessObject.$attrs.title
    const properties = {
      stage: stage,
      type: type,
      group: group,
      title: title
    }
    // 将属性写入shape和bpmn.xml中
    modeling.updateProperties(shape, properties)
  })

  BaseRenderer.call(this, eventBus, 1500)
}

// 继承
inherits(PhuketCustomRenderer, BaseRenderer)

// 实例化CustomRenderer并注入参数
PhuketCustomRenderer.$inject = ['eventBus', 'modeling']

/**
 * 是否可以渲染
 */
PhuketCustomRenderer.prototype.canRender = function(element) {
  element
  return true
  // return !element.labelTarget;
}

/**
 * 渲染Shape
 */
PhuketCustomRenderer.prototype.drawShape = function(visuals, shape) {
  console.log('drawShape', shape)
  // 如果是文本注释或标签则使用BaseRenderer.prototype.drawShape来渲染
  if (shape.type === 'bpmn:TextAnnotation' || shape.type === 'label') {
    // 使用BaseRenderer.prototype.drawShape来渲染
    return BaseRenderer.prototype.drawShape(visuals, shape)
  } else {
    // 自定义渲染Shape
    return drawCustomShape(visuals, shape)
  }
}

/**
 * 渲染Connection
 */
PhuketCustomRenderer.prototype.drawConnection = function(visuals, connection) {
  // 计算连线开始的点坐标和渲染条件序号
  const sourceShape = connection.source
  if (sourceShape) {
    const attrs = sourceShape.businessObject.$attrs
    const type = attrs.type
    // 源或执行器类型
    if (type === Constant.TYPE_SOURCE || type === Constant.TYPE_PROCESSOR) {
      const conditions = getShapeConditions(sourceShape)
      // 节点有条件信息，则执行如下程序
      if (conditions && conditions.length > 0) {
        const conditionNum = connection.businessObject.$attrs.conditionNum
        let i = 1
        if (conditionNum) {
          i = parseInt(conditionNum)
        } else {
          i = conditions.length
        }
        // 修改连线的条件序号值
        connection.businessObject.$attrs.conditionNum = i
        // 修改起点坐标
        const x = sourceShape.x
        const y = sourceShape.y
        const width = sourceShape.width
        const height = sourceShape.height
        const h = height / (conditions.length + 1)
        const outX = x + width
        const outY = y + h * i
        setWaypoint(connection, 0, outX, outY)
        // 添加渲染条件编号
        drawConditionNum(visuals, connection)
      } else {
        // 默认
        // 修改起点坐标
        const x = sourceShape.x
        const y = sourceShape.y
        const width = sourceShape.width
        const height = sourceShape.height
        const outX = x + width
        const outY = y + height / 2
        setWaypoint(connection, 0, outX, outY)
      }
    }
  }

  // 计算连线结束的点坐标
  const targetShape = connection.target
  if (targetShape) {
    const type = targetShape.businessObject.$attrs.type
    // 目标和执行器类型
    if (type === Constant.TYPE_TARGET || type === Constant.TYPE_PROCESSOR) {
      // 修改终点坐标
      const x = targetShape.x
      const y = targetShape.y
      const height = targetShape.height
      const inX = x
      const inY = y + height / 2
      const l = connection.waypoints.length
      setWaypoint(connection, l - 1, inX, inY)
    }
  }

  // 这里不能有返回结果或返回True，
  // 否则不会继续调用BpmnRenderer.prototype.drawConnection的方法来渲染Connection
}

// 设置路径点
function setWaypoint(connection, i, x, y) {
  connection.waypoints[i] = {
    original: { x: x, y: y },
    x: x,
    y: y
  }
}

/**
 * 添加渲染条件编号
 * @param {*} visuals
 * @param {*} connection
 */
function drawConditionNum(visuals, connection) {
  const conditionNum = connection.businessObject.$attrs.conditionNum
  if (!conditionNum || !isNumber(conditionNum)) return
  const ws = connection.waypoints
  const l = ws.length
  if (ws && l >= 2) {
    // 计算连线的中间点坐标
    const i = parseInt(l / 2)
    let x = 0
    let y = 0
    if (l % 2 === 1) {
      const w1 = ws[i - 1]
      const w2 = ws[i]
      const w3 = ws[i + 1]
      const x1 = (w1.x + w2.x) / 2
      const y1 = (w1.y + w2.y) / 2
      const x2 = (w2.x + w3.x) / 2
      const y2 = (w2.y + w3.y) / 2
      x = (x1 + x2) / 2
      y = (y1 + y2) / 2
    } else {
      const w1 = ws[i - 1]
      const w2 = ws[i]
      x = (w1.x + w2.x) / 2
      y = (w1.y + w2.y) / 2
    }
    // 渲染圆圈
    const circle = svgCreate('circle', {
      cx: x + 3,
      cy: y - 3,
      r: 8,
      fill: '#000000',
      stroke: '#8A8787',
      'stroke-width': 2
    })
    svgAppend(visuals, circle)
    // 渲染数值
    const text = svgCreate('text', {
      x: x,
      y: y,
      'font-size': 10,
      fill: '#ffffff',
      'font-weight': 700 // 加粗
    })
    text.innerHTML = conditionNum
    svgAppend(visuals, text)
  }
}

/**
 * 获取Shape路径
 */
PhuketCustomRenderer.prototype.getShapePath = function(shape) {
  if (shape) {
    // console.log(shape.businessObject.$attrs.type, shape);

    const x = shape.x
    const y = shape.y
    const width = shape.width
    const height = shape.height

    const type = shape.businessObject.$attrs.type

    let cx = x + width / 2
    const cy = y + height / 2
    const radius = 8

    if (type === Constant.TYPE_SOURCE) {
      cx = x + width
    } else if (type === Constant.TYPE_TARGET) {
      cx = x
    } else if (type === Constant.TYPE_PROCESSOR) {
      cx = x + width
    }

    var circlePath = [
      ['M', cx, cy],
      ['m', 0, -radius],
      ['a', radius, radius, 0, 1, 1, 0, 2 * radius],
      ['a', radius, radius, 0, 1, 1, 0, -2 * radius],
      ['z']
    ]

    return componentsToPath(circlePath)
  }
}

/**
 * 自定义渲染Shape
 *
 * @param {*} visuals
 * @param {*} shape
 */
function drawCustomShape(visuals, shape) {
  const businessObject = shape.businessObject
  const name = businessObject.name
  const attrs = businessObject.$attrs
  const stage = attrs.stage

  const url = Common.getRenderer(stage)

  const rectAttr = { width: 130, height: 90 } // 矩形宽和高
  const imageAttr = { width: 50, height: 50 } // 图标宽和高

  // 直接修改节点宽和高
  shape['width'] = rectAttr.width
  shape['height'] = rectAttr.height

  // 添加渲染底部矩形框
  addRect(visuals, shape, rectAttr)

  // 添加出入口圆圈
  addCircle(visuals, shape, rectAttr)

  // 添加渲染名称文本
  addText(visuals, name, rectAttr, imageAttr)

  // 添加渲染图标
  addImage(visuals, url, rectAttr, imageAttr)

  // 添加警告标识
  addWarning(visuals, shape, rectAttr)

  // 这里必须要有返回结果或返回True，
  // 否则还会继续调用BpmnRenderer.prototype.drawShape的方法再次渲染一遍
  return true
}

/**
 * 添加警告标识
 *
 * @param {*} visuals
 * @param {*} rectAttr
 */
function addWarning(visuals, shape, rectAttr) {
  let warning = false
  const messages = []
  const type = shape.businessObject.$attrs.type

  if (type === Constant.TYPE_TARGET || type === Constant.TYPE_PROCESSOR) {
    const incoming = shape.incoming
    if (incoming.length === 0) {
      warning = true
      messages.push('必须至少有一个输入关联')
    }
  }

  if (type === Constant.TYPE_SOURCE || type === Constant.TYPE_PROCESSOR) {
    const outgoing = shape.outgoing
    if (outgoing.length === 0) {
      warning = true
      messages.push('必须至少有一个输出关联')
    } else {
      const conditions = getShapeConditions(shape)
      if (conditions && conditions.length > 0) {
        const nums = []
        const conditionNums = {}
        for (let i = 0; i < outgoing.length; i++) {
          const conditionNum = outgoing[i].businessObject.$attrs.conditionNum
          conditionNums[conditionNum] = 1
        }
        for (let i = 1; i <= conditions.length; i++) {
          if (!conditionNums[i]) {
            warning = true
            nums.push(i)
          }
        }
        if (warning) {
          messages.push('存在开启的[' + nums.join(',') + ']输出没有关联')
        }
      }
    }
  }

  if (warning) {
    // 渲染警告标识
    const attr = { width: 24, height: 24 }
    const image = svgCreate('image', {
      x: rectAttr.width - attr.width / 2 - 10,
      y: rectAttr.height - attr.height / 2,
      width: attr.width,
      height: attr.height,
      href: require('@/assets/phuket/icons/exclamation-triangle.svg')
    })
    svgAppend(visuals, image)
    // 添加消息参数到shape
    shape.businessObject['$message'] = JSON.stringify(messages)
  } else {
    // 清空消息
    shape.businessObject['$message'] = null
  }
}

/**
 * 添加渲染底部矩形框
 *
 * @param {*} visuals
 * @param {*} shape
 * @param {*} rectAttr 矩形框参数
 */
function addRect(visuals, shape, rectAttr) {
  let fill = '#CCCCCC' // 填充色
  const model = shape.businessObject.$attrs['_model']
  if (Utils.isPreviewModel(model)) {
    const status = shape.businessObject.$attrs['_status']
    if (status === 'IDEL') {
      fill = '#CCCCCC' // 浅灰
    } else if (status === 'PROCESSING') {
      fill = '#428bca' // 浅蓝
    } else if (status === 'ERRORS') {
      fill = '#FF3333' // 浅红
    } else if (status === 'COMPLETED') {
      fill = '#5cb85c' // 浅绿
    } else if (status === 'STOP') {
      fill = '#FF8000' // 浅橙
    }
  }
  const rect = svgCreate('rect', {
    x: 0,
    y: 0,
    width: rectAttr.width,
    height: rectAttr.height,
    rx: 10, // x轴弯曲弧度
    ry: 10, // y轴弯曲弧度
    fill: fill, // 填充色
    'fill-opacity': '0.5', // 填充透明度
    stroke: '#8A8787', // 边框色
    'stroke-width': '2px' // 边框宽
  })
  svgAppend(visuals, rect)
}

/**
 * 先把中文替换成两个字节的英文，在计算长度
 *
 * @param {String} str
 */
function getLength(str) {
  return str.replace(/[\u0391-\uFFE5]/g, 'aa').length
}

/**
 * 添加渲染名称文本
 *
 * @param {*} visuals
 * @param {*} name
 * @param {*} rectAttr
 * @param {*} imageAttr
 */
function addText(visuals, name, rectAttr, imageAttr) {
  if (name) {
    const fontSize = 11
    const nameLength = getLength(name)
    const textWidth = (nameLength * fontSize) / 2 // 文字总宽度
    // 文字总宽度小于矩形宽
    if (textWidth < rectAttr.width) {
      const text = svgCreate('text', {
        x: (rectAttr.width - textWidth) / 2,
        y: imageAttr.height + (rectAttr.height - imageAttr.height) * (3 / 4),
        'font-size': fontSize,
        fill: 'black'
      })
      text.innerHTML = name
      svgAppend(visuals, text)
    } else if (textWidth >= rectAttr.width && textWidth / 2 < rectAttr.width) {
      // 文字总宽度大于矩形宽但小于两倍矩形宽
      const length = name.toString().length
      const halfLen = length / 2
      // 第一行
      const name1 = name.substring(0, halfLen)
      const nameLength1 = getLength(name1)
      const textWidth1 = (nameLength1 * fontSize) / 2
      const text1 = svgCreate('text', {
        x: (rectAttr.width - textWidth1) / 2,
        y:
          imageAttr.height +
          (rectAttr.height - imageAttr.height) * (1 / 4) +
          fontSize,
        'font-size': fontSize,
        fill: 'black'
      })
      text1.innerHTML = name1
      svgAppend(visuals, text1)
      // 第二行
      const name2 = name.substring(halfLen, length)
      const nameLength2 = getLength(name2)
      const textWidth2 = (nameLength2 * fontSize) / 2
      const text2 = svgCreate('text', {
        x: (rectAttr.width - textWidth2) / 2,
        y:
          imageAttr.height +
          (rectAttr.height - imageAttr.height) * (1 / 4) +
          fontSize * 2,
        'font-size': fontSize,
        fill: 'black'
      })
      text2.innerHTML = name2
      svgAppend(visuals, text2)
    } else {
      // 其他
      const lineNum = parseInt(textWidth / rectAttr.width) // 行数（取整）
      const remainder = textWidth % rectAttr.width // 余数
      const length =
        (name.toString().length - (remainder / fontSize) * 2) / lineNum
      // 第一行
      const name1 = name.substring(0, length)
      const nameLength1 = getLength(name1)
      const textWidth1 = (nameLength1 * fontSize) / 2 - 5
      const text1 = svgCreate('text', {
        x: (rectAttr.width - textWidth1) / 2,
        y:
          imageAttr.height +
          (rectAttr.height - imageAttr.height) * (1 / 4) +
          fontSize,
        'font-size': fontSize,
        fill: 'black'
      })
      text1.innerHTML = name1
      svgAppend(visuals, text1)
      // 第二行
      const name2 = name.substring(length, length * 2) + '...'
      const nameLength2 = getLength(name2) - 3
      const textWidth2 = (nameLength2 * fontSize) / 2
      const text2 = svgCreate('text', {
        x: (rectAttr.width - textWidth2) / 2,
        y:
          imageAttr.height +
          (rectAttr.height - imageAttr.height) * (1 / 4) +
          fontSize * 2,
        'font-size': fontSize,
        fill: 'black'
      })
      text2.innerHTML = name2
      svgAppend(visuals, text2)
    }
  }
}

/**
 * 添加渲染图标
 *
 * @param {*} visuals
 * @param {*} url
 * @param {*} rectAttr
 * @param {*} imageAttr
 */
function addImage(visuals, url, rectAttr, imageAttr) {
  const image = svgCreate('image', {
    x: (rectAttr.width - imageAttr.width) / 2,
    y: (rectAttr.height - imageAttr.height) / 4,
    width: imageAttr.width,
    height: imageAttr.height,
    href: url
  })
  svgAppend(visuals, image)
}

/**
 * 获取条件信息
 *
 * @param {*} shape
 */
function getShapeConditions(shape) {
  const attrs = shape.businessObject.$attrs
  let conditions = []
  for (const key in attrs) {
    if (key === Constant.NAME_CONDITIONS) {
      const value = attrs[key]
      if (value && value !== '') {
        conditions = JSON.parse(value)
      }
    }
  }
  return conditions
}

/**
 * 添加出入口圆圈
 *
 * @param {*} visuals
 * @param {*} shape
 * @param {*} rectAttr
 */
function addCircle(visuals, shape, rectAttr) {
  const conditions = getShapeConditions(shape)
  const type = shape.businessObject.$attrs.type
  // 渲染出口圆圈
  if (type === Constant.TYPE_SOURCE || type === Constant.TYPE_PROCESSOR) {
    if (conditions && conditions.length > 0) {
      // 条件渲染
      const length = conditions.length
      const height = rectAttr.height / (length + 1)
      for (let i = 0; i < conditions.length; i++) {
        // 渲染出口圆圈
        const outCircle = svgCreate('circle', {
          cx: rectAttr.width,
          cy: height * (i + 1),
          r: 8,
          fill: '#ffffff',
          'fill-opacity': '0.8',
          stroke: '#8A8787',
          'stroke-width': 2
        })
        svgAppend(visuals, outCircle)
        // 渲染出口索引值
        const outIndex = svgCreate('text', {
          x: rectAttr.width - 3,
          y: height * (i + 1) + 3,
          'font-size': 10,
          'font-weight': 700 // 加粗
        })
        outIndex.innerHTML = i + 1
        svgAppend(visuals, outIndex)
      }
    } else {
      // 默认渲染
      const outCircle = svgCreate('circle', {
        cx: rectAttr.width,
        cy: rectAttr.height / 2,
        r: 8,
        fill: '#ffffff',
        'fill-opacity': '0.8',
        stroke: '#8A8787',
        'stroke-width': 2
      })
      svgAppend(visuals, outCircle)
    }
  }
  // 渲染入口圆圈
  if (type === Constant.TYPE_TARGET || type === Constant.TYPE_PROCESSOR) {
    const inCircle = svgCreate('circle', {
      cx: 0,
      cy: rectAttr.height / 2,
      r: 8,
      fill: '#ffffff',
      'fill-opacity': '0.8',
      stroke: '#8A8787',
      'stroke-width': 2
    })
    svgAppend(visuals, inCircle)
  }
}
